package lambdasinaction.chap12.my;

import static java.time.temporal.TemporalAdjusters.lastDayOfMonth;
import static java.time.temporal.TemporalAdjusters.nextOrSame;

import java.time.DayOfWeek;
import java.time.Duration;
import java.time.Instant;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.Month;
import java.time.OffsetDateTime;
import java.time.Period;
import java.time.ZoneId;
import java.time.ZoneOffset;
import java.time.ZonedDateTime;
import java.time.chrono.ChronoLocalDate;
import java.time.chrono.Chronology;
import java.time.chrono.HijrahDate;
import java.time.chrono.IsoChronology;
import java.time.chrono.JapaneseDate;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeFormatterBuilder;
import java.time.temporal.ChronoField;
import java.time.temporal.ChronoUnit;
import java.time.temporal.Temporal;
import java.time.temporal.TemporalAdjuster;
import java.time.temporal.TemporalAdjusters;
import java.util.Date;
import java.util.Locale;

/**
 * TODO
 *
 * @author Fish
 * @since 0.0.1 2018/8/12 12:29
 */
public class MyDateTimeExamples {

  public static void main(String[] args) {

  }

  /**
   * 在 Java1.0中，对日期和时间的支持只能依赖java.util.Date 类。 正如类名所表达的，这个类无法表示日期，只能以毫秒的精度表示时间。
   * 更糟糕的是它的易用性，由于某些原因未知的设计决策， 这个类的易用性被深深地损害了， 比如：年份的起始选择是1900年，月份的起始从0开始。 这意味着，如果你想要用 Date 表示
   * Java8的发布日期，即2014年3月18日，需要创建下面这样的 Date 实例：
   *
   * 看起来不那么直观，不是吗？此外，甚至 Date 类的 toString 方法返回的字符串也容易误导人。 以我们的例子而言，它的返回值中甚至还包含了 JVM 的默认时区
   * CET，即中欧时间（Central Europe Time） 但这并不表示 Date 类在任何方面支持时区。
   */
  private void printOldDate() {
    Date date = new Date(114, 2, 18);
    System.out.println(date); // Fri Apr 18 00:00:00 CST 2014
  }

  /**
   * 使用 LocalDate 和 LocalTime 开始使用新的日期和时间 API 时，你最先碰到的可能是 LocalDate类。
   * 该类的实例是一个不可变对象，它只提供了简单的日期，并不含当天的时间信息。 另外，它也不附带任何与时区相关的信息。
   */
  private void playLocalDate() {
    /**
     * 通过静态工厂方法 of 创建一个 LocalDate 实例。
     * LocalDate 实例体用了多种方法来读取常用的值，比如年份、月份、星期几等
     */
    LocalDate date = LocalDate.of(2014, 3, 18); // 2014-03-18
    int year = date.getYear(); // 2014
    Month month = date.getMonth(); // MARCH
    int day = date.getDayOfMonth(); // 18
    DayOfWeek dow = date.getDayOfWeek(); // Tuesday
    int len = date.lengthOfMonth(); // 31 (days in March)

    /**
     * 你还可以使用工厂方法从系统时钟中获取当前的日期：
     */
    LocalDate today = LocalDate.now();

    /**
     * 通过传递一个 TemporalField 参数给 get 方法拿到同样的信息。
     * TemporalField 是一个接口，它定义了如何访问 temporal 对象某个字段的值。
     * ChronoField 枚举实现了这一接口，所以你可以很方便地使用 get 方法得到枚举元素的值。
     */
    int year = date.get(ChronoField.YEAR);
    int month = date.get(ChronoField.MONTH_OF_YEAR);
    int day = date.get(ChronoField.DAY_OF_MONTH);

    /**
     * 创建 LocalTime 读取一天中的时间，比如13：45：20
     * 使用 of 重载的两个工厂方法创建 LocalTime 的实例。
     * 第一个重载函数接收小时和分钟，第二个重载函数同时还接收秒。
     * 同 LocalDate 一样，LocalTime 类也提供了一些 getter 方法访问这些变量的值。
     */
    LocalTime time = LocalTime.of(13, 45, 20); // 13:45:20
    int hour = time.getHour(); // 13
    int minute = time.getMinute(); // 45
    int second = time.getSecond(); // 20

    /**
     * 使用静态方法 parse，解析字符串创建对象
     * 也可以向 parse 方法传递一个 DateTimeFormatter。该实例定义了如何格式化一个日期或者时间对象。 它是替换老版 java.util.DateFormat 的替代品。
     * 请注意，一旦传递的字符串参数无法被解析为合法的 LocalDate 或 LocalTime 对象，这两个 parse 方法都会抛出一个继承自 RuntimeException 的 DateTimeParseException 异常。
     */
    LocalDate date1 = LocalDate.parse("2014-03-18");
    LocalTime time1 = LocalTime.parse("13:45:20");

    /**
     * 合并日期和时间，这个复合类名叫 LocalDateTime。是 LocalDate 和 LocalTime 的合体。
     * 它同时表示了日期和时间，但不带有时区信息，你可以直接创建，也可以通过合并日期和时间对象构造。
     */
    LocalDateTime dateTime1 = LocalDateTime.of(2014, Month.MARCH, 18, 13, 45, 20); // 直接创建
    LocalDateTime dateTime2 = LocalDateTime.of(date, time); // 合并日期和时间对象

    /**
     * 通过各自的 atTime 或者 atDate 方法，向 LocalDate 传递一个时间对象，或者 LocalTime 传递一个日期对象的方式，你可以创建一个 LocalDateTime对象。
     */
    LocalDateTime dateTime3 = date.atTime(13, 45, 20);
    LocalDateTime dateTime4 = date.atTime(time);
    LocalDateTime dateTime5 = time.atDate(date);

    /**
     * 使用 toLocalDate 或者 toLocalTime 方法，从 LocalDateTime 中提取 LocalDate 或者 LocalTime组件
     */
    LocalDate date2 = dateTime1.toLocalDate(); // 2014-03-18
    LocalTime time2 = dateTime2.toLocalTime(); // 13:45:20
  }

  /**
   * 机器的日期和时间格式 作为人，我们习惯于以星期几、几号、几点、几分这样的方式理解日期和时间。 毫无疑问，这种方式对于计算机而言并不容易理解。
   * 从计算机的角度来看，建模时间最自然的格式是表示一个持续时间段上某个点的单一大整型数。 这也是新的 java.time.Instant 类对时间建模的方式，基本上它是以 Unix
   * 元年时间（传统的设定为 UTC 时区1970年1月1日午夜时分） 开始所经历的秒数进行计算。 你可以通过向静态工厂方法 ofEpochSecond 传递一个代表秒数的值创建一个该类的实例。
   * 静态工厂方法 ofEpochSecond 还有一个增强的重载版本，它接受第二个以纳秒为单位的参数值， 对传入作为秒数的参数进行调整。 重载的版本会调整纳秒参数，确保保存的纳秒分片在0到999
   * 999 999之间。 这意味着下面这些对 ofEpochSecond 工厂方法的调用会返回几乎同样的 Instant 对象
   *
   * Instant 的设计初衷是为了便于机器使用，它包含的是由秒及纳秒所构成的数字。
   */
  private void playInstant() {
    Instant.ofEpochSecond(3);
    Instant.ofEpochSecond(3, 0);
    Instant.ofEpochSecond(2, 1_000_000_000); // 2秒之后再加上100万纳秒（1秒）
    Instant.ofEpochSecond(4, -1_000_000_000); // 4秒之前的100万纳秒（1秒）
  }

  /**
   * 定义 Duration: 持续时间，主要用于以秒和纳秒衡量时间的长短。 可以创建两个 LocalTime 对象、两个 LocalDateTime 对象，或者两个 Instant
   * 对象之间的时间差 duration
   */
  private void playDuration() {
    LocalTime localTime1 = LocalTime.of(12, 12, 12);
    LocalTime localTime2 = LocalTime.of(12, 13, 12);
    Duration d1 = Duration.between(localTime1, localTime2);

    LocalDateTime localDateTime1 = LocalDateTime.of(2018, 7, 13, 23, 30, 30);
    LocalDateTime localDateTime2 = LocalDateTime.now();
    Duration d2 = Duration.between(localDateTime1, localDateTime2);

    Instant instant1 = Instant.ofEpochSecond(3);
    Instant instant2 = Instant.now();
    Duration d3 = Duration.between(instant1, instant2);
  }

  /**
   * 如果你需要以年、月或者日的方式对多个时间单位建模，可以使用 Period 类。 使用该类的工厂方法 between，你可以使用得到两个 LocalDate 之间的时长。
   */
  private void playPeriod() {
    Period manyDays = Period.between(LocalDate.of(2018, 7, 14), LocalDate.now());
  }

  /**
   * Duration 和 Period 类都提供了很多非常方便的工厂类 直接创建对应的实例，不再是只能以两个 temporal 对象的差值的方式来定义它们的对象
   */
  private void createDurationOrPeriod() {
    Duration threeMintes = Duration.ofMinutes(3);
    Duration threeMintes2 = Duration.of(3, ChronoUnit.MINUTES);

    Period tenDays = Period.ofDays(10);
    Period threeWeeks = Period.ofWeeks(3);
    Period twoYearsSixMonthsOneDay = Period.of(2, 6, 1);

    /**
     * Duration 类和 Period 类共享了很多相似的方法
     * 日期-时间类中表示时间间隔的通用方法
     * --方法名--    --是否是静态方法--  --方法描述--
     * between      是                 创建两个时间点之间的 interval (间距的意思)
     * from         是                 由一个临时时间点创建 interval
     * of           是                 由它的组成部分创建 interval 的实例
     * parse        是                 由字符串创建 interval 的实例
     * addTo        否                 创建该 interval 的副本，并将其叠加到某个指定的 temporal 对象
     * get          否                 读取该 interval 的状态
     * isNegative   否                 检查该 interval 是否为负值，不包含零
     * isZero       否                 检查该 interval 的时长是否为零
     * minus        否                 通过减去一定的时间创建该 interval 的副本
     * multipliedBy 否                 将 interval 的值乘以某个标量创建该 interval 的副本
     * negated      否                 以忽略某个时长的方式创建该 interval 的副本
     * plus         否                 以增加某个指定的时长的方式创建该 interval 的副本
     * subtractFrom 否                 从指定的 temporal 对象中减去该 interval
     */
  }

  /**
   * 截至目前，我们介绍的这些日期-时间对象都是不可修改的，这是为了更好地支持函数式编程，确保线程安全， 保持领域模式一致性而做出的重大设计决定。 当然，新的日期和时间 API
   * 也提供了一些便利的方法来创建这些对象的可变版本。 比如，你可能希望在已有的 LocalDate 实例上增加3天。 如果你已经有一个 LocalDate
   * 对象，想要创建它的一个修改版，最直接也最简单的方法是使用 withAttribute方法。 withAttribute 方法会创建对象的一个副本，并按照需要修改它的属性。
   * 注意，下面的这段代码中所有的方法都返回一个修改了属性的对象，它们都不会修改原来的对象！
   */
  private void doLocalDate() {
    // 以比较直观的方式操纵 LocalDate的属性
    LocalDate date1 = LocalDate.of(2014, 3, 18); // 2014-03-18
    LocalDate date2 = date1.withYear(2011); // 2011-03-18
    LocalDate date3 = date2.withDayOfMonth(25); // 2011-03-25

    /**
     * 采用更通用的 with 方法能达到同样的目的，它接受的第一个参数是一个 TemporalField 对象。
     */
    LocalDate date4 = date3.with(ChronoField.MONTH_OF_YEAR, 9); // 2011-09-25
  }

  /**
   * 以相对方式修改 LocalDate 对象的属性 使用的 plus 方法也是通用方法，它和 minus 方法都声明于 Temporal 接口中。 通过这些方法，对 TemporalUnit
   * 对象加上或者减去一个数字，我们能非常方便地将 Temporal 对象前溯或者回滚至某个时间段， 通过 ChronoUnit枚举我们可以非常方便地实现 TemporalUnit 接口
   */
  private void doLocalDate2() {
    LocalDate date1 = LocalDate.of(2014, 3, 18); // 2014-03-18
    LocalDate date2 = date1.plusWeeks(1); // 2014-03-25
    LocalDate date3 = date2.minusYears(3); // 2011-03-25
    LocalDate date4 = date3.plus(6, ChronoUnit.MONTHS); // 2011-09-25

    /**
     * LocalDate、LocalTime、LocalDateTime 以及 Instant 这样表示时间点的日期-时间类提供了大量通用的方法
     * 表示时间点的日期-时间类的通用方法
     * --方法名--  --是否是静态方法--  --描述--
     * from       是                 依据传入的 Temporal 对象创建对象实例
     * now        是                 依据系统时钟创建 Temporal 对象
     * of         是                 由 Temporal 对象的某个部分创建该对象的实例
     * parse      是                 由字符串创建 Temporal 对象的实例
     * atOffset   否                 将 Temporal 对象和某个时区偏移相结合
     * atZone     否                 将 Temporal 对象和某个时区相结合
     * format     否                 使用某个指定的格式器将 Temporal 对象转换为字符串（Instant 类不提供该方法）
     * get        否                 读取 Temporal 对象的某一部分的值
     * minus      否                 创建 Temporal 对象的一个副本，通过将当前 Temporal 对象的值减去一定的时长创建该副本
     * plus       否                 创建 Temporal 对象的一个副本，通过将当前 Temporal 对象的值加上一定的时长创建该副本
     * with       否                 以该 Temporal 对象为模板，对某些状态进行修改创建该对象的副本
     */
  }

  /**
   * 正如我们刚才看到的，你可以通过绝对的方式，也能以相对的方式操纵日期。 你甚至还可以在一个语句中连接多个操作，因为每个动作都会创建一个新的 LocalDate 对象，
   * 后续的方法调用可以操纵前一方法创建的对象。 这段代码的最后一句不会产生任何我们能看到的效果，因为它像前面的那些操作一样，会创建一个新的 LocalDate
   * 实例，不过我们并没有将这个新创建的值赋给任何的变量。
   */
  private void testLocalDate() {
    LocalDate date = LocalDate.of(2014, 3, 18); // 2014-03-18
    date = date.with(ChronoField.MONTH_OF_YEAR, 9); // 2014-09-18
    date = date.plusYears(2).minusDays(10); // 2016-09-18 - 10 = 2016-09-08
  }

  /**
   * 截止目前，你所看到的所有日期操作都是相对比较直接的。 有的时候，你需要进行一些更加复杂的操作 比如，将日期调整到下个周日、下个工作日，或者是本月的最后一天。 这是，你可以使用重载版本的
   * with 方法，向其传递一个提供了更多定制化选择的 TemporalAdjuster 对象， 更加灵活地处理日期。对于最常见的用例，日期和时间 API 已经提供了大量预定义的
   * TemporalAdjuster。 你可以通过 TemporalAdjuster 类的静态工厂方法访问它们。
   */
  private void playTemporalAdjuster() {
    LocalDate date1 = LocalDate.of(2014, 3, 18); // 2014-03-18
    LocalDate date2 = date1.with(nextOrSame(DayOfWeek.SUNDAY)); // 2014-03-23
    LocalDate date3 = date2.with(lastDayOfMonth()); // 2014-03-31

    /**
     * TemporalAdjuster 类中工厂方法列表
     * --方法名--            --秒数--
     * dayOfWeekInMonth     创建一个新的日期，它的值为同一个月中每一周的第几天
     * firstDayOfMonth      创建一个新的日期，它的值为当月的第一天
     * firstDayOfNextMonth  创建一个新的日期，它的值为下月的第一天
     * firstDayOfNextYear   创建一个新的日期，它的值为明年的第一天
     * firstDayOfYear       创建一个新的日期，它的值为当年的第一天
     * firstInMonth         创建一个新的日期，它的值为同一个月中，第一个符合星期几要求的值
     * lastDayOfMonth       创建一个新的日期，它的值为当月的最后一天
     * lastDayOfNextMonth   创建一个新的日期，它的值为下月的最后一天
     * lastDayOfNextYear    创建一个新的日期，它的值为明年的最后一天
     * lastDayOfYear        创建一个新的日期，它的值为今年的最后一天
     * lastInMonth          创建一个新的日期，它的值为同一个月中，最后一个符合星期几要求的值
     * next/previous        创建一个新的日期，并将其值设定为日期调整后或者调整前，第一个符合指定星期几要求的日期
     * nextOrSame/PreviousOrSame  创建一个新的日期，并将其值设定为日期调整后或者调整前，第一个符合指定星期几要去的日期，如果该日期已经符合要求，直接返回该对象
     */
  }

  /**
   * 如果你想要使用 Lambda 表达式定义 TemporalAdjuster 对象，推荐使用 TemporalAdjusters 类的静态工厂方法 ofDateAdjuster， 它接受一个
   * UnaryOperator<LocalDate>
   */
  private void createLambdaTemporalAdjuster() {
    LocalDate localDate = LocalDate.now();
    TemporalAdjuster nextWorkingDay = TemporalAdjusters.ofDateAdjuster(
        temporal -> {
          DayOfWeek dayOfWeek = DayOfWeek.of(temporal.get(ChronoField.DAY_OF_WEEK));
          int dayToAdd = 1;
          if (dayOfWeek == DayOfWeek.FRIDAY) {
            dayToAdd = 3;
          } else if (dayOfWeek == DayOfWeek.SATURDAY) {
            dayToAdd = 2;
          }
          return temporal.plus(dayToAdd, ChronoUnit.DAYS);
        }
    );
    localDate = localDate.with(nextWorkingDay);
  }

  /**
   * 打印输出及解析日期-时间对象 处理日期和时间对象时，格式化以及解析日期——时间对象是另一个非常重要的功能。 新的 java.time.format 包就是特别为这个目的而设计的。
   * 这个包中，最重要的类是 DateTimeFormatter。 创建格式器最简单的方法是通过它的静态工厂方法以及常量。 像 BASIC_ISO_DATE 和 ISO_LOCAL_DATE
   * 这样的常量是 DateTimeFormatter 类的预定义实例。 所有的 DateTimeFormatter 实例都能用于以一定的格式创建代表特定日期或时间的字符串。
   */
  private void printDate() {
    LocalDate date = LocalDate.of(2014, 3, 18);
    String s1 = date.format(DateTimeFormatter.BASIC_ISO_DATE); // 20140318
    String s2 = date.format(DateTimeFormatter.ISO_LOCAL_DATE); // 2014-03-18
  }

  /**
   * 通过工厂方法 parse 达到重新创建日期对象的目的 和老的 java.util.DateFormat 相比较，所有的 DateTimeFormatter 实例都是线程安全的。
   * 所有，你能够以单利模式创建格式器实例，就像 DateTimeFormatter 所定义的那些常量，并能在多个线程间共享这些实例。
   */
  private void parseDateStr() {
    LocalDate date1 = LocalDate.parse("20140318", DateTimeFormatter.BASIC_ISO_DATE);
    LocalDate date2 = LocalDate.parse("2014-03-18", DateTimeFormatter.ISO_LOCAL_DATE);
  }

  /**
   * DateTimeFormatter 类还支持一个静态工厂方法，它可以按照某个特定的模式创建格式器 这段代码中，LocalDate 的 format
   * 方法使用指定的模式生成了一个代表该日期的字符串。 紧接着，静态的 parse 方法使用同样的格式器解析了刚才生成的字符串，并重建了该日期对象。
   */
  private void createFormatter() {
    DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd/MM/yyyy");
    LocalDate date1 = LocalDate.of(2014, 3, 18);
    String formattedDate = date1.format(formatter);
    LocalDate date2 = LocalDate.parse(formattedDate, formatter);
  }

  /**
   * ofPattern 方法也提供了一个重载的版本，使用它你可以创建某个 Locale 的格式器
   */
  private void createFormatter2() {
    DateTimeFormatter italianFormatter = DateTimeFormatter
        .ofPattern("d. MMMM yyyy", Locale.ITALIAN); // 意大利的本地时间格式
    LocalDate date1 = LocalDate.of(2014, 3, 18);
    String formattedDate = date1.format(italianFormatter); // 18. marzo 2014
    LocalDate date2 = LocalDate.parse(formattedDate, italianFormatter);
  }

  /**
   * 更加细粒度的控制，DateTimeFormatterBuilder 类还提供了更复杂的格式器 你可以选择恰当的方法，一步一步地构造自己的格式器。
   * 另外，它还提供了非常强大的解析功能，比如区分大小写的解析、柔性解析（允许解析器使用启发式的机制去解析输入，不精确地匹配指定的模式）、填充 以及在格式器中指定可选节。
   */
  private void createFormatter3() {
    DateTimeFormatter italianFormatter = new DateTimeFormatterBuilder()
        .appendText(ChronoField.DAY_OF_MONTH)
        .appendLiteral(". ")
        .appendText(ChronoField.MONTH_OF_YEAR)
        .appendLiteral(" ")
        .appendText(ChronoField.YEAR)
        .parseCaseInsensitive()
        .toFormatter(Locale.ITALIAN);
  }

  /**
   * 处理不同的时区和历法 之前你看到的日期和时间的种类都不包含时区信息。 时区的处理是新版日期和时间 API 新增加的重要功能，使用新版日期和时间 API 时区的处理被极大地简化了。 新的
   * java.time.ZoneId类是老版 java.util.TimeZone的替代品。 它的设计目标就是要让你无需为时区处理的复杂和繁琐而担心，比如处理日光时（Daylight
   * Saving Time,DST）这种问题。 跟其他日期和时间类一样，ZoneId 类也是无法修改的。
   *
   * 时区是按照一定的规则将趋于划分成的标准时间相同的区间。 在 ZoneRules 这个类中包含了40个这样的实例。 你可以简单地通过调用 ZoneId的 getRules()
   * 得到指定时区的规则。 每个特定的 ZoneId 对象都由一个地区 ID 标识，比如： ZoneId romeZone = ZoneId.of("Europe/Rome"); 地区 ID
   * 都为"{区域}/{城市}"的格式，这些地区集合的设定都由英特网编号分配机构（IANA）的时区数据库提供。 你可以通过 Java8的新方法 toZoneId将一个老的时区对象转换为
   * ZoneId: ZoneId zoneId = TimeZone.getDefault().toZoneId(); 一旦得到一个 ZoneId 对象，你就可以将它与
   * LocalDate/LocalDateTime 或者是 Instant 对象整合起来，构造为一个 ZonedDateTime 实例，它代表了相对于指定时区的时间点。
   *
   * 为时间点添加时区信息
   */
  private void addZone() {
    ZoneId romeZone = ZoneId.of("Europe/Rome"); // 欧洲，罗马
    LocalDate date = LocalDate.of(2014, Month.MARCH, 18);
    ZonedDateTime zdt1 = date.atStartOfDay(romeZone);

    /**
     *
     * LocalDate + LocalTime + ZoneId = ZoneDateTime
     * LocalDate + LocalTime = LocalDateTime
     *
     */

    // 通过 ZoneId,你还可以将 LocalDateTime 转换为 Instant
    LocalDateTime dateTime = LocalDateTime.of(2014, Month.MARCH, 18, 13, 45);
    Instant instantFromDateTime = dateTime.toInstant((ZoneOffset) romeZone);
    // 你于可以通过反向的方式得到 LocalDateTime 对象
    Instant instant = Instant.now();
    LocalDateTime timeFromInstant = LocalDateTime.ofInstant(instant, romeZone);
  }

  /**
   * 利用和 UTC/格林尼治时间的固定偏差计算时区 另一种比较通用的表达时区的方式是利用当前时区和 UTC/格林尼治的固定偏差。 比如，基于这个理论，你可以说"纽约落后于伦敦5个小时"
   * 这种情况下，你可以使用 ZoneOffset 类，它是 ZoneId 的一个子类，表示的是当前时间和伦敦格林尼治子午线时间的差异
   */
  private void playZoneOffset() {
    ZoneOffset newYorkOffset = ZoneOffset.of("-05:00");
    /**
     * "-05:00"的偏差实际上对应是美国东部标准时间。
     * 注意，使用这种方式定义的 ZoneOffset 并未考虑任何时光时的影响，
     * 所以在大多数情况下，不推荐使用。
     * 由于 ZoneOffset 也是 ZoneId,
     * 你甚至还可以创建这样的 OffsetDateTime,它使用 ISO-8601的历法系统，以相对于 UTC/格林尼治时间的偏差方式表示日期和时间
     */
    LocalDateTime dateTime = LocalDateTime.of(2014, Month.MARCH, 18, 13, 45);
    OffsetDateTime dateTimeInNewYork = OffsetDateTime.of(dateTime, newYorkOffset);
    // 新版的日期和时间 API 还提供了另一个高级特性，即对非 ISO 历法系统（non-ISO calendaring）的支持。
  }

  /**
   * 使用别的日历系统 ISO-8601日历系统是世界文明日历系统的事实标准。 但是，Java8中另外还提供了4种其他的日历系统。 这些日历系统中的每一个都一个对应的日志类，分别是
   * ThaiBuddhistDate、MinguoDate、JapaneseDate、以及 HijrahDate。 所有这些类以及 LocalDate都实现了 ChronoLocalDate
   * 接口，能够对公历的日期进行建模。 利用 LocalDate 对象，你可以创建这些类的实例。 更通用地说，使用它们提供的静态工厂方法，你可以创建一个 Temporal 对象的实例：
   */
  private void playOtherCalendarSystem() {
    LocalDate date = LocalDate.of(2014, Month.MARCH, 18);
    JapaneseDate japaneseDate = JapaneseDate.from(date);
    /**
     * 为某个 Local 显示地创建日历系统，接着创建该 Local 对应的日期的实例。
     * 新的日期和时间 API 中，Chronology 接口建模了一个日历系统，使用它的静态工厂方法 ofLocale，可以得到它的一个实例
     */
    Chronology japaneseChronology = Chronology.ofLocale(Locale.JAPAN);
    ChronoLocalDate now = japaneseChronology.dateNow();
    /**
     * 日期及时间 API 的设计者建议我们使用 LocalDate，尽量避免使用 ChronoLocalDate。
     * 原因是开发者在他们的代码中可能会做一些假设，而这些假设在不同的日历系统中，有可能不成立。
     * 比如，有人可能会做这样的假设，即一个月天数不会超过31天，一年包括12个月，或者一年中包含的月份数目是固定的。
     * 由于这些原因，我们建议你尽量在你的应用中使用 LocalDate，包括存储、操作、业务规则的解决
     * 不过如果你需要将程序的输入或者输出本地化，这时你应该使用 ChronoLocalDate 类。
     */
  }

  /**
   * 伊斯兰教日历 在 Java8新添加的集中日历类型中，HijrahDate(伊斯兰教日历)是最复杂的一个，因为它会发生各种变化 Hijrah 日历系统构建与农历月份继承之上
   * Java8提供了多种方法判断一个月份，比如新月，在世界的哪些地方可见， 或者说它只能首先可见于沙特阿拉伯 withVariant 方法可以用于选择期望的变化。 为了支持 HijrahDate
   * 这一标准，Java8中还包括了乌姆库拉（Umm A1-Qura）变量 下面这段代码作为一个例子说明了如何在 ISO 日历中计算当前伊斯兰年中斋月的起始和终止日期
   */
  private void createHijrahDate() {
    HijrahDate ramadanDATE =
        HijrahDate.now().with(ChronoField.DAY_OF_MONTH, 1)
            .with(ChronoField.MONTH_OF_YEAR, 9); // 取得当前的 Hijrah 日期，紧接着对其进行修正，得到斋月的第一天，即第9个月
    System.out.println("Ramadan starts on" +
        IsoChronology.INSTANCE.date(ramadanDATE) + // IsoChronology.INSTANCE是 IsoChronology 类的一个静态实例
        " and ends on" +
        IsoChronology.INSTANCE.date(ramadanDATE.with(TemporalAdjusters.lastDayOfMonth()))); // 斋月始于2014-06-28，止于2014-07-27
  }


  /**
   * 正如我们看到的，使用 TemporalAdjuster我们可以进行更加复杂的日期操作，而且这些方法的名称也非常直观， 方法名基本就是问题陈述。此外，即使你没有找到符合你要求的预定义的
   * TemporalAdjuster，创建你自己的 TemporalAdjuster 也并非难事。 实际上，TemporalAdjuster
   * 接口只声明了单一的一个方法（这使得它成为了一个函数式接口）
   *
   * @FunctionalInterface public interface TemporalAdjuster { Temporal adjustInto(Temporal
   * temporal); } 这意味着 TemporalAdjuster 接口的实现需要定义如何将一个 Temporal 对象转换为另一个 Temporal对象。 你可以把它看成一个
   * UnaryOperator<Temporal>
   *
   * 实现一个定制的 TemporalAdjuster 请设计一个 NextWorkingDay 类，该类实现了 TemporalAdjuster 接口，能够计算明天的日期，
   * 同时过滤掉周六和周日这些节假日。 格式如下所示：date = date.with(new NextWorkingDay()); 如果当天的星期介于周一至周五之间，日期向后移动一天；
   * 如果当天是周六或者周日，则返回下一个周一。
   */
  public class NextWorkingDay implements TemporalAdjuster {

    @Override
    public Temporal adjustInto(Temporal temporal) {
      // 读取当前日期
      DayOfWeek dayOfWeek = DayOfWeek.of(temporal.get(ChronoField.DAY_OF_WEEK));
      int dayToAdd = 1; // 正常情况，增加1天
      if (dayOfWeek == DayOfWeek.FRIDAY) {
        dayToAdd = 3; // 如果当天是周五，增加3天
      } else if (dayOfWeek == DayOfWeek.SATURDAY) {
        dayToAdd = 2; // 如果当天是周六，增加2天
      }
      return temporal.plus(dayToAdd, ChronoUnit.DAYS); // 增加恰当的天数后，返回修改的日期
    }
  }
}

